//#define __FORCE_TRACE__
//#define __FORCE_DEBUG__
//#define __FORCE_INFO__
#define THIS_FILE "framework\fn_updateTask.sqf"
#include <tmf_constants.h>
#include <tmf_macros.h>

TRACE_1("Start",_this);
TMFPROFILERSTART;
private ["_taskParams","_taskUnique","_taskTitle","_taskDesc","_taskWPDesc","_taskWPPos","_taskStatus","_unitTasks","_taskPos","_currentItem","_foundTask","_i"
		,"_taskSuccessVar","_taskFailedVar","_taskFailedInvertVar","_taskContent","_taskInstance","_instance_content","_mission_tasks","_jip_instance","_jip_tasks"
		,"_mission_name","_mission_description","_mission_id","_mission_task","_taskGroup","_isForMyGroup"];
DEFAULT_PARAM(_taskParams,0,[]);
DEFAULT_PARAM(_taskInstance,1,TMF_SERVER_INSTANCE_ID);
DEFAULT_PARAM(_taskReloaded,2,false);
_currentItem = [];

if (_taskInstance == "") then {_taskInstance = TMF_SERVER_INSTANCE_ID;};

_taskUnique =	GETVAL(_taskParams,0,"");
_taskTitle =	GETVAL(_taskParams,1,"");
_taskDesc =		GETVAL(_taskParams,2,"");
_taskWPDesc =	GETVAL(_taskParams,3,"");
_taskWPPos =	GETVAL(_taskParams,4,ARR_3(0,0,0));
_taskStatus =	GETVAL(_taskParams,5,"Created");
_taskSuccessVar=GETVAL(_taskParams,6,"");
_taskFailedVar=	GETVAL(_taskParams,7,"");
_taskFailedInvertVar=GETVAL(_taskParams,8,"");
_taskGroup=GETVAL(_taskParams,9,"");

if (_taskUnique == "") exitWith {};

switch (toUpper(_taskStatus)) do {
	case "CREATED": { _taskStatus = "Created";};
	case "ASSIGNED": { _taskStatus = "Assigned";};
	case "CURRENT": { _taskStatus = "Assigned";};
	case "SUCCEEDED": { _taskStatus = "Succeeded";};
	case "FAILED": { _taskStatus = "Failed";};
	case "CANCELED": { _taskStatus = "Canceled";};
	case "CANCELLED": { _taskStatus = "Canceled";};
	case "TASKNEW": { _taskStatus = "Created";};
	case "TASKDONE": { _taskStatus = "Succeeded";};
	case "TASKFAILED": { _taskStatus = "Failed";};
	case "TASKCURRENT": { _taskStatus = "Assigned";};
	case "TASKCREATED": { _taskStatus = "Created";};
	case "TASKASSIGNED": { _taskStatus = "Assigned";};
	case "TASKSUCCEEDED": { _taskStatus = "Succeeded";};
	case "TASKFAILED": { _taskStatus = "Failed";};
	case "TASKCANCELED": { _taskStatus = "Canceled";};
	default { _taskStatus = "";};
};

// converts a numerical objective to a text one
if (!(IS_STRING(_taskSuccessVar)) && {IS_SCALAR(_taskSuccessVar)}) then {
	_taskSuccessVar = format[TMF_EDITOR_OBJECTIVE,_taskSuccessVar];
	_taskParams set [6,_taskSuccessVar];
};
// converts a numerical objective to a text one
if (!(IS_STRING(_taskFailedVar)) && {IS_SCALAR(_taskFailedVar)}) then {
	_taskFailedVar = format[TMF_EDITOR_OBJECTIVE,_taskFailedVar];
	_taskParams set [7,_taskFailedVar];
};
// converts a numerical objective to a text one
if (!(IS_STRING(_taskFailedInvertVar)) && {IS_SCALAR(_taskFailedInvertVar)}) then {
	_taskFailedInvertVar = format[TMF_EDITOR_OBJECTIVE,_taskFailedInvertVar];
	_taskParams set [8,_taskFailedInvertVar];
};


if (isnil QGVAR(UPDATE_TASK_BUSY)) then { GVAR(UPDATE_TASK_BUSY) = -1; };
waituntil {(GVAR(UPDATE_TASK_BUSY) < time)};
GVAR(UPDATE_TASK_BUSY) = time + 10;

_foundTask = [];

_fnc_update_task_params = {
	private ["_task_src","_task_dst","_val1","_val2"];
	TRACE_1("Start Task Update",_this);
	_task_dst = _this select 0;
	_task_src = _this select 1;
	if (isNil "_task_src" || isNil "_task_dst") then {
		ERROR_1("Invalid Parameter",_this);
	} else {
		if (!IS_ARRAY(_task_src) || !IS_ARRAY(_task_dst)) then {
			ERROR_1("Invalid Parameter",_this);
		} else {
			//Task Title
			_val1 = _task_dst select 1;
			_val2 = _task_src select 1;
			if (_val1 == "") then { _task_dst set[1,_val2] };
			//Task Desc
			_val1 = _task_dst select 2;
			_val2 = _task_src select 2;
			if (IS_ARRAY(_val1)) then {
				if (count _val1 <= 0) then {
					_task_dst set[2,_val2];
				};
			} else {
				if (_val1 == "") then { _task_dst set[2,_val2] };
			};
			//Task WPDesc
			_val1 = _task_dst select 3;
			_val2 = _task_src select 3;
			if (_val1 == "") then { _task_dst set[3,_val2] };
			//Task WPPos
			_val1 = _task_dst select 4;
			_val2 = _task_src select 4;
			if (OR_ELSE(OR_ELSE(!IS_ARRAY(_val1),(count _val1 !=3)),((_val1 distance ARR_3(0,0,0)) <= 0))) then { _task_dst set[4,_val2] };
			//Task Status
			_val1 = _task_dst select 5;
			_val2 = _task_src select 5;
			if (_val1 == "Created" && _val2 != "") then {  _task_dst set[5,_val2] };
			// Task Group
			if (count _task_src > 9) then {
				_task_dst set [9, (_task_src select 9)];
			};
			TRACE_1("Updated Task",_task_dst);
		};
	};
	_task_dst;
};

// 1. We create/update the task in the JIP variable, this allows for recreating tasks on spawn/respawn/jip.
If (GVAR(Server)) then {
	_taskPos = -1;
	TRACE_1("Start with server",_taskUnique);
	if (isnil QGVAR(STORING_OBJECT_BUSY)) then { GVAR(STORING_OBJECT_BUSY) = -1; };
	waituntil {sleep (random 1); (GVAR(STORING_OBJECT_BUSY) < time)};
	GVAR(STORING_OBJECT_BUSY) = time + 10;
	// The following is already done in getMissionInstance
	// _instances = JIPGETVAR(QGVAR(MISSION_INSTANCES),[]);
	// if (!(_taskInstance in _instances)) then {
		// _instances set [count _instances, _taskInstance];
		// JIPSETVAR(QGVAR(MISSION_INSTANCES),_instances);
		// TRACE_1("Instances",_instances);
	// };
	
	_jip_instance = [_taskInstance] call FFUNC(getMissionInstance);
	_jip_tasks = [_jip_instance, TMF_HK_MISSION_TASKS,[],THIS_FILE] call FFUNC(hashGet);
	
	TRACE_1("JIP Tasks",_jip_tasks);
	if (!isNil "_jip_tasks") then {
		if (count _jip_tasks > 0) then {
			for "_i" from 0 to ((count _jip_tasks) - 1) step 1 do {
				_currentItem = _jip_tasks select _i;
				if (str(_currentItem select 0) == str(_taskUnique)) exitWith {_taskPos = _i;};
			};
		};
	} else {
		_jip_tasks = [];
	};
	if (_taskPos < 0) then {
		_foundTask = [];
		_foundTask set [0, _taskUnique];
		_foundTask set [1, _taskParams];
		_jip_tasks set [count _jip_tasks, _foundTask];
		TRACE_1("Adding Task",_taskUnique);
	} else {
		_foundTask = _jip_tasks select _taskPos;
		// We update the infos
		_taskParams = [_taskParams, (_foundTask select 1)] call _fnc_update_task_params;
		// We save the updated infos
		_foundTask set [1, _taskParams];
		_jip_tasks set [_taskPos, _foundTask];
		TRACE_1("Updating Task",_foundTask);
	};
	_jip_instance = [_jip_instance, TMF_HK_MISSION_TASKS,_jip_tasks] call FFUNC(HashSet);
	[_taskInstance,_jip_instance] call FFUNC(setMissionInstance);
	GVAR(STORING_OBJECT_BUSY) = -1;
	
	if (_taskPos < 0 && _taskStatus == "Created") then {
		[_taskInstance, _taskParams] spawn {
			private ["_taskInstance","_taskParams","_success","_failure","_failureInvert","_taskUnique","_taskTitle","_taskDesc"
					,"_taskWPDesc","_taskWPPos","_taskStatus","_taskSuccessVar","_taskFailedVar","_taskFailedInvertVar"];
			TRACE_1("Status Watcher Start",_this);
			DEFAULT_PARAM(_taskInstance,0,TMF_SERVER_INSTANCE_ID);
			DEFAULT_PARAM(_taskParams,1,"");
			
			_taskUnique =	GETVAL(_taskParams,0,"");
			_taskTitle =	GETVAL(_taskParams,1,"");
			_taskDesc =		GETVAL(_taskParams,2,"");
			_taskWPDesc =	GETVAL(_taskParams,3,"");
			_taskWPPos =	GETVAL(_taskParams,4,ARR_3(0,0,0));
			_taskStatus =	GETVAL(_taskParams,5,"Created");
			_taskSuccessVar=GETVAL(_taskParams,6,"");
			_taskFailedVar=	GETVAL(_taskParams,7,"");
			_taskFailedInvertVar=GETVAL(_taskParams,8,"");
		
			if (_taskUnique != "" AND ((_taskSuccessVar != "") OR (_taskFailedVar !="") OR (_taskFailedInvertVar != ""))) then {
				TRACE_1("Start Waiting For Change",ARR_3(_taskSuccessVar,_taskFailedVar,_taskFailedInvertVar));
				waituntil {sleep 5.68795; 
					(
						(if (_taskSuccessVar != "") then {([_taskInstance,_taskSuccessVar,false] call FFUNC(getMissionJIPVar))} else {false}) 
					 OR (if (_taskFailedVar != "") then {([_taskInstance,_taskFailedVar,false] call FFUNC(getMissionJIPVar))} else {false})
					 OR (if (_taskFailedInvertVar != "") then {(!([_taskInstance,_taskFailedInvertVar,true] call FFUNC(getMissionJIPVar)))} else {false})
					);
				};
				if ((_taskSuccessVar != "") AND ([_taskInstance,_taskSuccessVar,false] call FFUNC(getMissionJIPVar))) then {
					[[_taskUnique,"", "","",[],"Succeeded"],_taskInstance,false] call FFUNC(Task);
					// [[_taskUnique,_taskTitle, _taskDesc,_taskWPDesc,_taskWPPos,"Succeeded"],_taskInstance,false] call FFUNC(Task);
				} else {
					if ((_taskFailedVar != "") AND ([_taskInstance,_taskFailedVar,false] call FFUNC(getMissionJIPVar))) then {
						[[_taskUnique,"", "","",[],"Failed"],_taskInstance,false] call FFUNC(Task);
					} else {	
						if ((_taskFailedInvertVar != "") AND (!([_taskInstance,_taskFailedInvertVar,true] call FFUNC(getMissionJIPVar)))) then {
							[[_taskUnique,"", "","",[],"Failed"],_taskInstance,false] call FFUNC(Task);
						};
					};
				};
			};
			TRACE_1("Status Watcher End",_taskUnique);
		};
	};
	TRACE_1("Done with server",ARR_2(_taskUnique,GVAR(STORING_OBJECT_BUSY)));
};

// 3. We create/update the task on the client side
If (GVAR(Client)) then {
	// Ensuring we dont have task updates colliding
	TRACE_1("Start with client",_taskUnique);
	if (isnil QGVAR(STORING_OBJECT_BUSY)) then { GVAR(STORING_OBJECT_BUSY) = -1; };
	waituntil {sleep (random 1); (GVAR(STORING_OBJECT_BUSY) < time)};
	GVAR(STORING_OBJECT_BUSY) = time + 10;
	_isForMyGroup = true;
	if (IS_STRING(_taskGroup) && {_taskGroup != ""}) then {
		_isForMyGroup = (_taskGroup == str(group player)) || (_taskGroup == str(playerSide));
	};
	// We never display hints for a task which is not for the player group
	_taskReloaded = (_taskReloaded && _isForMyGroup) || (!_isForMyGroup);
	TRACE_1("Task for my group",ARR_3(_taskReloaded,_taskGroup,_isForMyGroup));
	
	_instance_content = PGETVAR(_taskInstance,(call FFUNC(hashEmpty)));
	_unitTasks = [_instance_content, TMF_HK_MISSION_TASKS,[],THIS_FILE] call FFUNC(hashGet);

	_mission_task = taskNull;
	_mission_description = [_taskInstance, TMF_HK_MISSION_DESC,[]] call FFUNC(getMissionVar);
	_mission_id = GETVAL(_mission_description,TMF_MD_ID,-1);
	if (_mission_id >= 0) then {
		TRACE_1("Has Mission Description",_mission_id);
		_mission_task = [_instance_content, TMF_HK_MISSION_TASK,taskNull,THIS_FILE] call FFUNC(hashGet);
		
		if (isnull _mission_task) then {
			_mission_unique = format["mission_task_unique_name_%1_%2",_taskInstance,_mission_id];
			_mission_name = GETVAL(_mission_description,TMF_MD_NAME,"STR_TMF_DEFAULT_MISSIONNAME");
			_mission_desc = GETVAL(_mission_description,TMF_MD_DESC,"");
			_mission_task = player createSimpleTask [_mission_unique];
			_instance_content = [_instance_content, TMF_HK_MISSION_TASK,_mission_task] call FFUNC(HashSet);
			TRACE_1("Created Mission Task",ARR_3(_mission_task, _mission_name, _mission_desc));
			_mission_task setSimpleTaskDescription [_mission_desc,_mission_name,""];
		};
	};
	_taskPos = -1;
	TRACE_1("Unit tasks",_unitTasks);
	if (count _unitTasks > 0) then {
		for "_i" from 0 to ((count _unitTasks) - 1) step 1 do {
			_currentItem = _unitTasks select _i;
			if (str(_currentItem select 0) == str(_taskUnique)) exitWith {_taskPos = _i;};
		};
	};
	_foundTask = taskNull;
	
	If (_taskPos < 0) then {
		if (_isForMyGroup) then {
			if (isnull _mission_task) then {
				_foundTask = player createSimpleTask [_taskUnique];
			} else {
				_foundTask = player createSimpleTask [_taskUnique,_mission_task];
			};
		};
		_unitTasks set [count _unitTasks, [_taskUnique,_foundTask,_taskParams]];
		_taskPos = count _unitTasks;
	} else {
		if (_isForMyGroup) then {
			_foundTask = (_unitTasks select _taskPos) select 1;
			TRACE_2("Task State",str(taskState _foundTask),_taskUnique);
			_taskReloaded = (_taskReloaded || (toupper(str(taskState _foundTask)) == toUpper(_taskStatus)));
			if (str(taskState _foundTask) == "None") then { 
				if (isnull _mission_task) then {
					_foundTask = player createSimpleTask [_taskUnique];
				} else {
					_foundTask = player createSimpleTask [_taskUnique,_mission_task];
				};
			};
		};
		TRACE_1("Task Before Update",(_unitTasks select _taskPos));
		// We update the infos
		_taskParams = [_taskParams, ((_unitTasks select _taskPos) select 2)] call _fnc_update_task_params;
	};
	
	_taskUnique =	GETVAL(_taskParams,0,"");
	_taskTitle =	GETVAL(_taskParams,1,"");
	_taskDesc =		GETVAL(_taskParams,2,"");
	_taskWPDesc =	GETVAL(_taskParams,3,"");
	_taskWPPos =	GETVAL(_taskParams,4,ARR_3(0,0,0));
	_taskStatus =	GETVAL(_taskParams,5,"Created");
	
	if (_taskDesc == "") then {
		_taskDesc = ["STR_TMF_TASKNODESC",_taskTitle];
	};
	
	_taskTitle = _taskTitle call FFUNC(Translate);
	_taskDesc = _taskDesc call FFUNC(Translate);
	if (_taskWPDesc != "") then {
		_taskWPDesc = _taskWPDesc call FFUNC(Translate);
	};
	
	if (_isForMyGroup) then {
		If (_taskTitle != "") then {
			_foundTask setSimpleTaskDescription [_taskDesc,_taskTitle,_taskWPDesc];
		};
		if (count _taskWPPos > 0) then {
			if ((_taskWPPos vectorDistance [0,0,0]) > 0) then {
				_foundTask setSimpleTaskDestination _taskWPPos;
			};
		};
		_foundTask setTaskState _taskStatus;
	};
	
	_unitTasks set [_taskPos, [_taskUnique, _foundTask, _taskParams]];
	[_instance_content, TMF_HK_MISSION_TASKS,_unitTasks] call FFUNC(HashSet);
	PSETVAR(_taskInstance,_instance_content);
	TRACE_1("Updating tasks on client",ARR_3(_taskUnique,count _unitTasks, _taskReloaded));
	
	GVAR(STORING_OBJECT_BUSY) = -1;
	
	// The _taskReloaded check ensures that it's not a task added on spawn/respawn but a real task update
	if (!_taskReloaded && _isForMyGroup) then	{
		_taskContent = (taskDescription _foundTask);
		_taskDisplay = _taskTitle;
		if (typename _taskContent == typename []) then {
			if (count _taskContent > 1) then { _taskDisplay = _taskContent select 1; };
		};
		TRACE_1("Show Notification",_taskStatus);
		if (_taskStatus != "") then {
			[_taskDisplay,_taskStatus] call FFUNC(notification);
		};
	};
	TRACE_1("Done with client",ARR_2(_taskUnique,GVAR(STORING_OBJECT_BUSY)));
};
GVAR(UPDATE_TASK_BUSY) = -1;
TRACE("End");
TMFPROFILERSTOP;